#include "ConnectionPool.h"
#include "public.h"
#include <iostream>
using namespace std;

ConnectionPool *ConnectionPool::getConnectionPool()
{
    static ConnectionPool pool;
    return &pool;
}

// 加载配置文件
bool ConnectionPool::loadConfig()
{
    FILE *fp = fopen("mysql.conf", "r");
    if (fp == nullptr)
    {
        LOG("mysql.conf don't exist!");
        return false;
    }
    while (!feof(fp))
    {
        char line[1024] = {0};
        fgets(line, 1024, fp);
        string str = line;
        int idx = str.find('=', 0);
        if (idx == -1)
        {
            continue;
        }
        int endidx = str.find('\n', idx);
        string key = str.substr(0, idx);
        string value = str.substr(idx + 1, endidx - idx - 1);
        key.erase(key.find_last_not_of(" \n\r\t") + 1);
        value.erase(value.find_last_not_of(" \n\r\t") + 1);

        if (key == "ip")
        {
            _ip = value;
        }
        else if (key == "port")
        {
            _port = static_cast<unsigned short>(stoi(value));
        }
        else if (key == "username")
        {
            _username = value;
        }
        else if (key == "password")
        {
            _password = value;
        }
        else if (key == "dbname")
        {
            _dbname = value;
        }
        else if (key == "initSize")
        {
            _initSize = stoi(value);
        }
        else if (key == "maxSize")
        {
            _maxSize = stoi(value);
        }
        else if (key == "maxIdleTime")
        {
            _maxIdleTime = stoi(value);
        }
        else if (key == "connectionTimeout")
        {
            _connectionTimeout = stoi(value);
        }
    }
    fclose(fp);
    return true;
}

// 连接池的构造函数
ConnectionPool::ConnectionPool()
{
    if (!loadConfig())
    {
        return;
    }
    // 创建初始连接：创建连接，建立连接，入队，计数
    for (int i = 0; i < _initSize; i++)
    {
        Connection *conn = new Connection();
        conn->refreshAliveTime();
        conn->connect(_ip, _port, _username, _password, _dbname);
        _connQueue.push(conn);
        _connCnt++;
    }
    // 启用一个新线程，作为连接的生产者
    thread produce(bind(&ConnectionPool::produceConnectionTask, this));
    produce.detach();

    // 创建一个扫描线程，定时释放连接
    thread scanner(bind(&ConnectionPool::scanConnectionTask, this));
    scanner.detach();
}

// 独立线程产生新连接
void ConnectionPool::produceConnectionTask()
{
    for (;;)
    {
        unique_lock<mutex> lock(_queueMutex); // 生产者加锁，notifyall之后是否锁
        while (!_connQueue.empty())
        {
            cv.wait(lock); // 队列不为空，生成线程进行等待状态
        }
        if (_connCnt < _maxSize)
        {
            Connection *conn = new Connection();
            conn->refreshAliveTime();
            conn->connect(_ip, _port, _username, _password, _dbname);
            _connQueue.push(conn);
            _connCnt++;
        }
        cv.notify_all(); // 通知消费者线程可以工作
    }
}

// 给外部调用，获取连接
shared_ptr<Connection> ConnectionPool::getConnection()
{
    unique_lock<mutex> lock(_queueMutex);
    while (_connQueue.empty())
    {
        if (cv_status::timeout == cv.wait_for(lock, chrono::microseconds(_connectionTimeout)))
        {
            LOG("获取连接超时");
            return nullptr;
        }
    }

    /* 自定义析构函数，将连接归还线程队列
     */
    shared_ptr<Connection> sp(_connQueue.front(),
                              [&](Connection *pcon)
                              {
                                  unique_lock<mutex> lock(_queueMutex);
                                  pcon->refreshAliveTime();
                                  _connQueue.push(pcon);
                              });
    _connQueue.pop();
    // 通知生产者检查线程队列是否为空
    cv.notify_all();
    return sp;
}

// 定时扫描释放多余的线程
void ConnectionPool::scanConnectionTask()
{
    for (;;)
    {
        this_thread::sleep_for(chrono::seconds(_maxIdleTime));
        unique_lock<mutex> lock(_queueMutex);
        // 看队列头部是否超过最大空闲时间，超过就删掉
        while (_connCnt > _initSize)
        {
            Connection *p = _connQueue.front();
            if (p->getAliveTime() >= (_maxIdleTime * 1000))
            {
                _connQueue.pop();
                _connCnt--;
                delete p;
            }
            else
                break;
        }
    }
}